package matrixMultiplicationAnalysis.Cache;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CacheSimulator {
    private final int cacheSize;      // 缓存总大小（字节）
    private final int blockSize;      // 缓存行宽度（字节）
    private final int numBlocks;      // 缓存行数量
    private final CacheBlock[] cache; // 缓存存储数组

    // 矩阵配置
    private final int matrixElementSize = 4; // 每个矩阵元素4个字节(int)
    private int matrixCols; // 矩阵的列数（用于计算地址）
    
    // 缓存块结构
    private static class CacheBlock {
        boolean valid;      // 有效位
        int tag;           // 标签位
        int accessTime;    // 最后访问时间（用于LRU）
        int loadAddress;   // 加载的起始地址（用于调试）
        
        public CacheBlock() {
            this.valid = false;
            this.tag = -1;
            this.accessTime = 0;
            this.loadAddress = -1;
        }
    }
    
    // 统计信息
    private int hits;
    private int misses;
    private int evictions;
    private int currentTime;
    
    // 详细的访问记录（用于分析访问模式）
    private List<AccessRecord> accessRecords;
    
    // 文件输出相关
    private String experimentName;
    private String matrixType; // 新增：标识是A、B还是C矩阵
    private static PrintWriter resultWriter;
    private static PrintWriter detailWriter;
    
    static {
        try {
            // 初始化结果文件，写入CSV头部
            resultWriter = new PrintWriter(new FileWriter("cache_results.csv", true));
            // detailWriter = new PrintWriter(new FileWriter("cache_access_details.csv", true));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 访问记录类
    private static class AccessRecord {
        int address;
        int row;
        int col;
        boolean hit;
        int timestamp;
        
        public AccessRecord(int address, int row, int col, boolean hit, int timestamp) {
            this.address = address;
            this.row = row;
            this.col = col;
            this.hit = hit;
            this.timestamp = timestamp;
        }
    }
    
    public CacheSimulator(int cacheSize, int blockSize, int matrixCols, String experimentName, String matrixType) {
        this.cacheSize = cacheSize;
        this.blockSize = blockSize;
        this.matrixCols = matrixCols;
        this.numBlocks = cacheSize / blockSize;
        this.experimentName = experimentName;
        this.matrixType = matrixType;
        this.cache = new CacheBlock[numBlocks];
        this.accessRecords = new ArrayList<>();
        
        // 初始化所有缓存块
        for (int i = 0; i < numBlocks; i++) {
            cache[i] = new CacheBlock();
        }
        
        this.hits = 0;
        this.misses = 0;
        this.evictions = 0;
        this.currentTime = 0;
    }
    
    /**
     * 计算地址对应的缓存索引和标签
     */
    private CacheAddress parseAddress(int address) {
        int blockOffset = address % blockSize;  // 在缓存行内的偏移
        int blockNumber = address / blockSize;  // 内存中的块编号
        int index = blockNumber % numBlocks;    // 缓存索引（直接映射）
        int tag = blockNumber / numBlocks;      // 使用块编号作为tag
        
        return new CacheAddress(tag, index, blockOffset);
    }
    
    /**
     * 模拟缓存行加载：加载包含目标地址的整个缓存行
     */
    private void loadCacheLine(int startAddress) {
        CacheAddress addr = parseAddress(startAddress);
        CacheBlock block = cache[addr.index];

        currentTime++;
        
        // 如果需要替换（有效位为true）
        if (block.valid && block.tag != addr.tag) {
            evictions++;
        }
        
        // 加载新的缓存行
        block.valid = true;
        block.tag = addr.tag;
        block.accessTime = currentTime;
        block.loadAddress = startAddress;
    }
    
    /**
     * 处理矩阵元素访问
     */
    public boolean accessMatrixElement(int row, int col) {
        // 计算矩阵元素的内存地址：行优先存储
        // if(this.matrixType.equals("A")){
        //     System.out.println(row+" "+col);
        // }
        int elementAddress = (row * matrixCols + col) * matrixElementSize;
        boolean hit = accessMemory(elementAddress);
        
        // 记录详细的访问信息
        accessRecords.add(new AccessRecord(elementAddress, row, col, hit, currentTime));
        
        return hit;
    }
    
    /**
     * 处理内存访问请求
     */
    public boolean accessMemory(int address) {
        currentTime++;
        CacheAddress addr = parseAddress(address);
        CacheBlock block = cache[addr.index];
        // if(this.matrixType.equals("A")){
        //     System.out.println(address+" "+addr.tag+" "+block.tag);
        // }
        // 检查是否命中
        if (block.valid && block.tag == addr.tag) {
            hits++;
            block.accessTime = currentTime;
            return true;
        }
        
        misses++;
        int cacheLineStart = (address / blockSize) * blockSize;
        loadCacheLine(cacheLineStart);
        return false;
    }

    /**
     * 获取缓存统计状态
     */
    public int[] getStatus() {
        int[] ans = new int[3];
        ans[0] = hits;
        ans[1] = misses;
        ans[2] = evictions;
        return ans;
    }
    
    /**
     * 获取详细的统计信息
     */
    public Map<String, Object> getDetailedStats() {
        Map<String, Object> stats = new HashMap<>();
        int totalAccesses = hits + misses;
        
        stats.put("matrixType", matrixType);
        stats.put("hits", hits);
        stats.put("misses", misses);
        stats.put("evictions", evictions);
        stats.put("totalAccesses", totalAccesses);
        stats.put("hitRate", totalAccesses > 0 ? (double)hits / totalAccesses : 0.0);
        stats.put("missRate", totalAccesses > 0 ? (double)misses / totalAccesses : 0.0);
        stats.put("cacheSize", cacheSize);
        stats.put("blockSize", blockSize);
        stats.put("matrixCols", matrixCols);
        stats.put("experimentName", experimentName);
        
        // 计算空间局部性指标（连续访问相同缓存行的比例）
        stats.put("spatialLocality", calculateSpatialLocality());
        
        return stats;
    }
    
    /**
     * 计算空间局部性指标
     */
    private double calculateSpatialLocality() {
        if (accessRecords.size() < 2) return 0.0;
        
        int spatialHits = 0;
        for (int i = 1; i < accessRecords.size(); i++) {
            AccessRecord prev = accessRecords.get(i-1);
            AccessRecord curr = accessRecords.get(i);
            
            // 检查是否访问了相同的缓存行
            int prevBlock = prev.address / blockSize;
            int currBlock = curr.address / blockSize;
            
            if (prevBlock == currBlock) {
                spatialHits++;
            }
        }
        
        return (double) spatialHits / (accessRecords.size() - 1);
    }

    /**
     * 将统计结果写入CSV文件
     */
    public void writeResultsToCSV(String loopOrder, int matrixSize) {
        try {
            Map<String, Object> stats = getDetailedStats();
            
            // 写入汇总结果
            String csvLine = String.format("%s,%s,%d,%d,%d,%s,%d,%d,%d,%.4f,%.4f,%.4f",
                    experimentName,
                    loopOrder,
                    matrixSize,
                    cacheSize,
                    blockSize,
                    matrixType,
                    stats.get("hits"),
                    stats.get("misses"),
                    stats.get("evictions"),
                    stats.get("hitRate"),
                    stats.get("missRate"),
                    stats.get("spatialLocality"));
            
            synchronized(resultWriter) {
                resultWriter.println(csvLine);
                resultWriter.flush();
            }
            
            // 写入详细的访问记录
            // writeDetailedAccessRecords(loopOrder, matrixSize);
            
        } catch (Exception e) {
            System.err.println("写入CSV文件时出错: " + e.getMessage());
        }
    }
    
    /**
     * 写入详细的访问记录
     */
    // private void writeDetailedAccessRecords(String loopOrder, int matrixSize) {
    //     try {
    //         for (AccessRecord record : accessRecords) {
    //             String detailLine = String.format("%s,%s,%d,%d,%s,%d,%d,%d,%b,%d",
    //                     experimentName,
    //                     loopOrder,
    //                     matrixSize,
    //                     cacheSize,
    //                     matrixType,
    //                     record.row,
    //                     record.col,
    //                     record.address,
    //                     record.hit,
    //                     record.timestamp);
                
    //             synchronized(detailWriter) {
    //                 detailWriter.println(detailLine);
    //             }
    //         }
    //         detailWriter.flush();
            
    //     } catch (Exception e) {
    //         System.err.println("写入详细访问记录时出错: " + e.getMessage());
    //     }
    // }
    
    /**
     * 初始化CSV文件头部（只需要执行一次）
     */
    public static void initializeCSVFiles() {
        try {
            // 汇总结果文件表头
            PrintWriter writer = new PrintWriter(new FileWriter("cache_results.csv"));
            writer.println("experiment_name,loop_order,matrix_size,cache_size,block_size,matrix_type,hits,misses,evictions,hit_rate,miss_rate,spatial_locality");
            writer.close();

            // 详细访问记录文件表头
            writer = new PrintWriter(new FileWriter("cache_access_details.csv"));
            writer.println("experiment_name,loop_order,matrix_size,cache_size,block_size,matrix_type,row,col,address,hit,timestamp");
            writer.close();

            // 重新以追加模式打开
            resultWriter = new PrintWriter(new FileWriter("cache_results.csv", true));
            // detailWriter = new PrintWriter(new FileWriter("cache_access_details.csv", true));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
// ...existing code...
    
    /**
     * 关闭文件写入器
     */
    public static void closeResultWriters() {
        if (resultWriter != null) {
            resultWriter.close();
        }
        if (detailWriter != null) {
            detailWriter.close();
        }
    }

    /**
     * 打印统计信息到控制台
     */
    public void printStatistics() {
        Map<String, Object> stats = getDetailedStats();
        System.out.println("=== " + experimentName + " - 矩阵" + matrixType + " 缓存统计 ===");
        System.out.println("缓存大小: " + stats.get("cacheSize") + " 字节");
        System.out.println("缓存行大小: " + stats.get("blockSize") + " 字节");
        System.out.println("矩阵列数: " + stats.get("matrixCols"));
        System.out.println("总访问次数: " + stats.get("totalAccesses"));
        System.out.println("命中次数: " + stats.get("hits"));
        System.out.println("缺失次数: " + stats.get("misses"));
        System.out.println("替换次数: " + stats.get("evictions"));
        System.out.println("命中率: " + String.format("%.2f%%", (double)stats.get("hitRate") * 100));
        System.out.println("缺失率: " + String.format("%.2f%%", (double)stats.get("missRate") * 100));
        System.out.println("空间局部性: " + String.format("%.2f%%", (double)stats.get("spatialLocality") * 100));
    }
    
    /**
     * 重置缓存状态
     */
    public void reset() {
        for (CacheBlock block : cache) {
            block.valid = false;
            block.tag = -1;
            block.accessTime = 0;
            block.loadAddress = -1;
        }
        accessRecords.clear();
        hits = 0;
        misses = 0;
        evictions = 0;
        currentTime = 0;
    }

    // 内部类：缓存地址解析结果
    private static class CacheAddress {
        final int tag;
        final int index;
        final int offset;

        public CacheAddress(int tag, int index, int offset) {
            this.tag = tag;
            this.index = index;
            this.offset = offset;
        }
    }
}